<?php

namespace Db;

/**
 * Operatory logiczne
 * 
 * @author Michał Bocian <bocian.michal@outlook.com>
 * @version 1.0
 */
class Operators {

    /**
     *
     * @var string Zwraca ciąg z operatorami logicznymi
     */
    protected $return = '';
    protected $_escaping = 1;

    /**
     * Tworzy obiekt Operators
     * 
     * @return \Db\Operators Zwraca obiekt klasy Operators
     */
    public static function Create() {
        return new Operators();
    }

    /**
     * Dodaje do ciągu operator logiczny
     * 
     * @param string $operator Operator logiczny
     * @return \Db\Operators
     */
    public function O($operator) {
        $this->return .= "{$operator} ";
        return $this;
    }

    /**
     * Dodaje wartość do kolumny
     * 
     * @param string $variable Wartość
     * @return \Db\Operators
     */
    public function V($variable) {
        $this->return .= "'{$variable}' ";
        return $this;
    }

    /**
     * Dodaje kolumnę do ciągu
     * 
     * @param string $column kolumna
     * @return \Db\Operators
     */
    public function C($column) {
        $this->return .= Query::ConvertPathToString($column) . " ";
        return $this;
    }

    public function turnOffEscaping($option = true) {
        if ($option)
            $this->_escaping = 0;
        else
            $this->_escaping = 1;
    }

    /**
     * Zwraca ciąg logiczny
     * 
     * @return string Ciąg logiczny
     */
    public function Make() {
        return substr($this->return, 0, -1);
    }

    /**
     * 
     * @return string Ciąg logiczny
     */
    public function __toString() {
        return $this->Make();
    }

}

/**
 * Klasa tworzenia Kwerend SQL
 */
class Query {

    const MYSQL_QUERY = 1;/** SQL dla MySQL */
    const PGSQL_QUERY = 2;/** SQL dla PgSQL */

    /**
     *
     * @var string Rodzaj bazy danych
     */
    protected $dbType;

    /**
     *
     * @var string Rodzaj komendy SQL
     */
    protected $command;

    /**
     *
     * @var array Kolumny
     */
    protected $columns;

    /**
     *
     * @var string Kwerenda SQL
     */
    protected $statement;

    /**
     *
     * @var string Tabela
     */
    protected $table;

    /**
     *
     * @var string Kwerenda Where
     */
    protected $where_stmt;

    /**
     *
     * @var string Kwerenda Order By
     */
    protected $order_stmt;

    /**
     *
     * @var string Kwerenda Set
     */
    protected $set_stmt;

    /**
     *
     * @var string Kwerenda Limit
     */
    protected $limit_offset;

    /**
     *
     * @var string Kwerenda Create
     */
    protected $create_table;

    /**
     *
     * @var string Kwerenda Insert
     */
    protected $insert_stmt;

    /**
     * Tworzy obiekt Query
     * 
     * @param string $table Tabela
     * @param string $dbType Rodzaj SZBD
     * @return \Db\Query
     */
    public function __construct($table, $dbType) {
        $this->table = $table;
        $this->dbType = $dbType;
        return $this;
    }

    /**
     * Zwraca obiekt Query
     * 
     * @param string $table Tabela
     * @param string $dbType Rodzaj SZBD
     * @return \Db\Query
     */
    public static function Create($table, $dbType = \Application\Config::QueryDbSys) {
        return new Query($table, $dbType);
    }

    /**
     * Konwertuje sekwencję kolumn do SQL
     * 
     * @param array $sequence Sekwencja kolumn
     * @return string Sekwencja SQL
     */
    public static function ConvertSeqToString(array $sequence) {
        if (!empty($sequence)) {
            $return = '';
            foreach ($sequence as $part) {
                $return.="\"{$part}\", ";
            }
        } else {
            $return = '*  ';
        }
        return substr($return, 0, -2);
    }

    /**
     * Konwertuje sekwencję dostępu do SQL
     * 
     * @param string $path sekwencja
     * @return string Sekwencja SQL
     */
    public static function ConvertPathToString($path) {
        $return = '';
        foreach (explode('.', $path) as $part) {
            $return .= "\"{$part}\".";
        }
        return substr($return, 0, -1);
    }

    ///
    /// KWERENDY SQL
    ///

    /**
     * SQL: Select
     * 
     * @param array $columns Kolumny
     * @return \Db\Query Instancja obiektu Query
     */
    public function Select(array $columns = []) {
        $this->command = 'Select';
        $this->columns = $columns;
        return $this;
    }

    /**
     * SQL: Delete
     * 
     * @return \Db\Query Instancja obiektu Query
     */
    public function Delete() {
        $this->command = 'Delete';
        return $this;
    }

    /**
     * SQL: Update
     * 
     * @return \Db\Query Instancja obiektu Query
     */
    public function Update() {
        $this->command = 'Update';
        return $this;
    }

    /**
     * SQL: Insert
     * 
     * @param \Db\Operators $values Wartosci dla kolumn
     * @param array $columns Kolumny
     * @return \Db\Query Instancja obiektu Query
     */
    public function Insert(Operators $values, array $columns = []) {
        $this->command = 'Insert';
        $this->columns = $columns;
        $this->insert_stmt = $values->Make();
        return $this;
    }

    /**
     * SQL: Create Table
     * 
     * @param \Db\Operators $values Kolumny i ich typy
     * @return \Db\Query Instancja obiektu Query
     */
    public function CreateTable(Operators $operators) {
        $this->command = 'CTable';
        $this->create_table = $operators->Make();
        return $this;
    }

    ///
    /// FILTRY
    ///

    /**
     * SQL: Limit
     * 
     * @param type $offset Ograniczenie górne
     * @param type $limit Ilość reordów w dół
     * @return \Db\Query Instancja obiektu Query
     */
    public function Limit($offset, $limit) {
        if ($this->dbType == self::MYSQL_QUERY) {
            $this->limit_offset = "Limit {$offset},{$limit}";
        } else {
            $this->limit_offset = "Limit {$limit} Offset {$offset}";
        }
        return $this;
    }

    /**
     * SQL: OrderBy
     * 
     * @param \Db\Operators $operators Zdanie logiczne
     * @return \Db\Query Instancja obiektu Query
     */
    public function OrderBy(Operators $operators) {
        $this->order_stmt = "{$operators->Make()}";
        return $this;
    }

    /**
     * SQL: Set
     * 
     * @param \Db\Operators $operators Zdanie logiczne
     * @return \Db\Query Instancja obiektu Query
     */
    public function Set(Operators $operators) {
        $this->set_stmt = "{$operators->Make()}";
        return $this;
    }

    /**
     * SQL: Where
     * 
     * @param \Db\Operators $operators Zdanie logiczne
     * @return \Db\Query Instancja obiektu Query
     */
    public function Where(Operators $operators) {
        $this->where_stmt = "{$operators->Make()}";
        return $this;
    }

    ///
    /// ZWRACACZE
    ///

    /**
     * Zwraca kwerendę SQL
     * 
     * @return string Zwraca kwerendę SQL
     */
    public function Make() {
        switch ($this->command) {
            case 'Select':
                $this->statement = "Select " . self::ConvertSeqToString($this->columns) . " ";
                $this->statement .= "From " . self::ConvertPathToString($this->table) . " ";
                if (!is_null($this->where_stmt))
                    $this->statement .= "Where {$this->where_stmt} ";
                if (!is_null($this->order_stmt))
                    $this->statement .= "Order By {$this->order_stmt} ";
                if (!is_null($this->limit_offset))
                    $this->statement .= "{$this->limit_offset} ";
                break;
            case 'Delete':
                $this->statement = "Delete ";
                $this->statement .= "From " . self::ConvertPathToString($this->table) . " ";
                if (!is_null($this->where_stmt))
                    $this->statement .= "Where {$this->where_stmt} ";
                break;
            case 'Update':
                $this->statement = "Update ";
                $this->statement .= self::ConvertPathToString($this->table) . " ";
                if (!is_null($this->set_stmt))
                    $this->statement .= "Set {$this->set_stmt} ";
                if (!is_null($this->where_stmt))
                    $this->statement .= "Where {$this->where_stmt} ";
                break;
            case 'Insert':
                $this->statement = "Insert Into ";
                $this->statement .= self::ConvertPathToString($this->table) . " ";
                if (count($this->columns) > 0)
                    $this->statement.= "(" . self::ConvertSeqToString($this->columns) . ") ";
                $this->statement .= "values ({$this->insert_stmt})";
                break;
            case 'CTable':
                $this->statement = "Create Table ";
                $this->statement .= self::ConvertPathToString($this->table) . " (";
                $this->statement .= $this->create_table . ") ";
                break;
            default:
                $this->statement = 'Please specify query type';
                throw new Exception('Please specify query type');
                break;
        }
        return $this->statement;
    }

    /**
     * Zwraca kwerendę SQL
     * 
     * @return string Zwraca kwerendę SQL
     */
    public function __toString() {
        return $this->Make();
    }

}